package com.dupf.upms.rpc.service.impl;

import com.dupf.common.db.DataSourceEnum;
import com.dupf.common.db.DynamicDataSource;
import com.dupf.common.util.ItvJsonUtil;
import com.dupf.common.util.MD5Util;
import com.dupf.common.util.RedisUtil;
import com.dupf.common.util.Tools;
import com.dupf.upms.common.constant.UpmsConstant;
import com.dupf.upms.dao.mapper.*;
import com.dupf.upms.dao.model.*;
import com.dupf.upms.dto.UpmsUserResultDTO;
import com.dupf.upms.rpc.api.*;
import com.dupf.upms.rpc.mapper.UpmsApiMapper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * UpmsApiService实现
 * Created by dupf on 2018/3/1.
 */
@Service
@Transactional
public class UpmsApiServiceImpl implements UpmsApiService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UpmsApiServiceImpl.class);

    @Autowired
    UpmsUserMapper upmsUserMapper;

    @Autowired
    UpmsApiMapper upmsApiMapper;

    @Autowired
    UpmsRolePermissionMapper upmsRolePermissionMapper;

    @Autowired
    UpmsUserPermissionMapper upmsUserPermissionMapper;

    @Autowired
    UpmsSystemMapper upmsSystemMapper;

    @Autowired
    UpmsOrganizationMapper upmsOrganizationMapper;

    @Autowired
    UpmsLogMapper upmsLogMapper;

    @Autowired
    UpmsUserOrganizationMapper upmsUserOrganizationMapper;

    @Autowired
    QiniuUploadServiceImpl qiniuUploadServiceImpl;

    @Autowired
    UpmsUserOrganizationService upmsUserOrganizationService;

    @Autowired
    UpmsRoleService upmsRoleService;

    @Autowired
    UpmsUserRoleService upmsUserRoleService;

    @Autowired
    UpmsUserService upmsUserService;

    /**
     * 根据用户id获取所拥有的权限
     *
     * @param upmsUserId
     * @return
     */
    @Override
    public List<UpmsPermission> selectUpmsPermissionByUpmsUserId(Integer upmsUserId) {
        // 用户不存在或锁定状态
        UpmsUser upmsUser = upmsUserMapper.selectByPrimaryKey(upmsUserId);
        if (null == upmsUser || 1 == upmsUser.getLocked()) {
            LOGGER.info("selectUpmsPermissionByUpmsUserId : upmsUserId={}", upmsUserId);
            return null;
        }
        List<UpmsPermission> upmsPermissions = upmsApiMapper.selectUpmsPermissionByUpmsUserId(upmsUserId);
        return upmsPermissions;
    }

    /**
     * 根据用户id获取所拥有的权限
     *
     * @param upmsUserId
     * @return
     */
    @Override
    @Cacheable(value = "dupf-upms-rpc-service-ehcache", key = "'selectUpmsPermissionByUpmsUserId_' + #upmsUserId")
    public List<UpmsPermission> selectUpmsPermissionByUpmsUserIdByCache(Integer upmsUserId) {
        return selectUpmsPermissionByUpmsUserId(upmsUserId);
    }

    /**
     * 根据用户id获取所属的角色
     *
     * @param upmsUserId
     * @return
     */
    @Override
    public List<UpmsRole> selectUpmsRoleByUpmsUserId(Integer upmsUserId) {
        // 用户不存在或锁定状态
        UpmsUser upmsUser = upmsUserMapper.selectByPrimaryKey(upmsUserId);
        if (null == upmsUser || 1 == upmsUser.getLocked()) {
            LOGGER.info("selectUpmsRoleByUpmsUserId : upmsUserId={}", upmsUserId);
            return null;
        }
        List<UpmsRole> upmsRoles = upmsApiMapper.selectUpmsRoleByUpmsUserId(upmsUserId);
        return upmsRoles;
    }

    /**
     * 根据用户id获取所属的角色
     *
     * @param upmsUserId
     * @return
     */
    @Override
    @Cacheable(value = "dupf-upms-rpc-service-ehcache", key = "'selectUpmsRoleByUpmsUserId_' + #upmsUserId")
    public List<UpmsRole> selectUpmsRoleByUpmsUserIdByCache(Integer upmsUserId) {
        return selectUpmsRoleByUpmsUserId(upmsUserId);
    }

    /**
     * 根据角色id获取所拥有的权限
     *
     * @param upmsRoleId
     * @return
     */
    @Override
    public List<UpmsRolePermission> selectUpmsRolePermisstionByUpmsRoleId(Integer upmsRoleId) {
        UpmsRolePermissionExample upmsRolePermissionExample = new UpmsRolePermissionExample();
        upmsRolePermissionExample.createCriteria()
                .andRoleIdEqualTo(upmsRoleId);
        List<UpmsRolePermission> upmsRolePermissions = upmsRolePermissionMapper.selectByExample(upmsRolePermissionExample);
        return upmsRolePermissions;
    }

    /**
     * 根据用户id获取所拥有的权限
     *
     * @param upmsUserId
     * @return
     */
    @Override
    public List<UpmsUserPermission> selectUpmsUserPermissionByUpmsUserId(Integer upmsUserId) {
        UpmsUserPermissionExample upmsUserPermissionExample = new UpmsUserPermissionExample();
        upmsUserPermissionExample.createCriteria()
                .andUserIdEqualTo(upmsUserId);
        List<UpmsUserPermission> upmsUserPermissions = upmsUserPermissionMapper.selectByExample(upmsUserPermissionExample);
        return upmsUserPermissions;
    }

    /**
     * 根据条件获取系统数据
     *
     * @param upmsSystemExample
     * @return
     */
    @Override
    public List<UpmsSystem> selectUpmsSystemByExample(UpmsSystemExample upmsSystemExample) {
        return upmsSystemMapper.selectByExample(upmsSystemExample);
    }

    /**
     * 根据条件获取组织数据
     *
     * @param upmsOrganizationExample
     * @return
     */
    @Override
    public List<UpmsOrganization> selectUpmsOrganizationByExample(UpmsOrganizationExample upmsOrganizationExample) {
        return upmsOrganizationMapper.selectByExample(upmsOrganizationExample);
    }

    /**
     * 根据username获取UpmsUser
     *
     * @param username
     * @return
     */
    @Override
    public UpmsUser selectUpmsUserByUsername(String username) {
        UpmsUserExample upmsUserExample = new UpmsUserExample();
        upmsUserExample.createCriteria()
                .andUsernameEqualTo(username);
        List<UpmsUser> upmsUsers = upmsUserMapper.selectByExample(upmsUserExample);
        if (null != upmsUsers && upmsUsers.size() > 0) {
            return upmsUsers.get(0);
        }
        return null;
    }

    /**
     * 获取用户基本信息，常用信息
     *
     * @param username
     * @return
     */
    @Override
    public JSONObject selectUpmsUserInfoByUsername(String username) {
        JSONObject result;

        // 清楚缓存
        String userInfoRedis = RedisUtil.get(UpmsConstant.USER_INFO_REDIS_PREFIX + username);
        if (StringUtils.isEmpty(userInfoRedis)) {

            UpmsUserExample upmsUserExample = new UpmsUserExample();
            upmsUserExample.createCriteria()
                    .andUsernameEqualTo(username);
            List<UpmsUser> upmsUsers = upmsUserMapper.selectByExample(upmsUserExample);
            UpmsUser upmsUser;
            UpmsUserOrganization upmsUserOrganization;
            UpmsOrganization upmsOrganization;
            if (null != upmsUsers && upmsUsers.size() > 0) {
                upmsUser = upmsUsers.get(0);
                result = JSONObject.fromObject(upmsUser);

                UpmsUserOrganizationExample upmsUserOrganizationExample = new UpmsUserOrganizationExample();
                upmsUserOrganizationExample.createCriteria().andUserIdEqualTo(upmsUser.getUserId());
                List<UpmsUserOrganization> upmsUserOrganizations = upmsUserOrganizationMapper.selectByExample(upmsUserOrganizationExample);
                if (null != upmsUserOrganizations && upmsUserOrganizations.size() > 0) {
                    upmsUserOrganization = upmsUserOrganizations.get(0);
                    UpmsOrganizationExample upmsOrganizationExample = new UpmsOrganizationExample();
                    upmsOrganizationExample.createCriteria().andOrganizationIdEqualTo(upmsUserOrganization.getOrganizationId());
                    List<UpmsOrganization> upmsOrganizations = upmsOrganizationMapper.selectByExample(upmsOrganizationExample);
                    if (null != upmsOrganizations && upmsOrganizations.size() > 0) {
                        upmsOrganization = upmsOrganizations.get(0);
                        result.put("organizationId", upmsOrganization.getOrganizationId());
                        result.put("organizationName", upmsOrganization.getName());
                    }
                }

                UpmsUserRoleExample userRoleExample = new UpmsUserRoleExample();
                userRoleExample.createCriteria().andUserIdEqualTo(upmsUser.getUserId());
                UpmsUserRole upmsUserRole = upmsUserRoleService.selectFirstByExample(userRoleExample);
                if (upmsUserRole != null) {
                    UpmsRole upmsRole = upmsRoleService.selectByPrimaryKey(upmsUserRole.getRoleId());
                    if (upmsRole != null) {
                        result.put("roleId", upmsRole.getRoleId());
                        result.put("role", upmsRole.getName());
                    }
                }

                RedisUtil.set(UpmsConstant.USER_INFO_REDIS_PREFIX + username, result.toString(), UpmsConstant.USER_INFO_REDIS_EXPIRE * 60 * 60);

                return result;
            }
        } else {
            return JSONObject.fromObject(userInfoRedis);
        }
        return null;
    }

    @Override
    public List<UpmsRole> getOperableRoles(Integer userId) {

        // 当前用户所有角色
        List<UpmsRole> upmsRoles = selectUpmsRoleByUpmsUserId(userId);
        Integer minRoleId = Integer.MAX_VALUE;
        for (UpmsRole upmsRole : upmsRoles) {
            minRoleId = Math.min(minRoleId, upmsRole.getRoleId());
        }

        UpmsRoleExample roleExample = new UpmsRoleExample();
        List<UpmsRole> roles = upmsRoleService.selectByExample(roleExample);
        final Integer roleId = minRoleId;
        return roles.stream().filter(p -> p.getRoleId() > roleId).collect(Collectors.toList());
    }

    /**
     * 获取角色可管理的员工列表
     *
     * @param userName
     * @param orgId
     * @return
     */
    @Override
    public List<UpmsUser> getOperableUserList(String userName, Integer orgId) {
        UpmsUser upmsUser = selectUpmsUserByUsername(userName);
        List<UpmsUser> resultList = new ArrayList<>();
        resultList.add(upmsUser);
        List<UpmsRole> upmsRoles = getOperableRoles(upmsUser.getUserId());
        if (CollectionUtils.isEmpty(upmsRoles)) {
            return resultList;
        }
        List<Integer> operableRoleIds = upmsRoles.stream().map(UpmsRole::getRoleId).collect(Collectors.toList());

        List<Integer> userIds = upmsUserOrganizationService.getUserIdsByOrgId(orgId);

        if (CollectionUtils.isNotEmpty(userIds) && CollectionUtils.isNotEmpty(operableRoleIds)) {


            UpmsUserRoleExample userRoleExample = new UpmsUserRoleExample();
            userRoleExample.createCriteria().andRoleIdIn(operableRoleIds).andUserIdIn(userIds);
            List<UpmsUserRole> userRoles = upmsUserRoleService.selectByExample(userRoleExample);
            List<Integer> operableUserIds = userRoles.stream().map(UpmsUserRole::getUserId).distinct().filter(p -> !p.equals(upmsUser.getUserId())).collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(operableUserIds)) {
                UpmsUserExample upmsUserExample = new UpmsUserExample();
                upmsUserExample.createCriteria().andUserIdIn(operableUserIds);
                List<UpmsUser> operableList = upmsUserMapper.selectByExample(upmsUserExample);
                resultList.addAll(operableList);
            }
        }
        return resultList;
    }

    @Override
    public List<UpmsUser> getOperableUserListByCache(String userName, Integer orgId) {
        // 缓存中获取
        String operableListRedis = RedisUtil.get(UpmsConstant.USER_OPERABLE_LIST_REDIS_PREFIX + userName);

        if (StringUtils.isEmpty(operableListRedis)) {

            List<UpmsUser> upmsUsers = getOperableUserList(userName, orgId);
            JSONArray result = JSONArray.fromObject(upmsUsers);
            RedisUtil.set(UpmsConstant.USER_OPERABLE_LIST_REDIS_PREFIX + userName, result.toString(), UpmsConstant.USER_OPERABLE_LIST_REDIS_EXPIRE * 60);
            return upmsUsers;
        } else {
            return ItvJsonUtil.jsonToList(operableListRedis, UpmsUser.class);
        }
    }

    @Override
    public List<UpmsUser> getOrgUserList(Integer orgId) {
        // 缓存中获取
        String operableListRedis = RedisUtil.get(UpmsConstant.ORG_USER_LIST_REDIS_PREFIX + orgId);

        if (StringUtils.isEmpty(operableListRedis)) {

            UpmsUserOrganizationExample upmsUserOrganizationExample = new UpmsUserOrganizationExample();
            upmsUserOrganizationExample.createCriteria().andOrganizationIdEqualTo(orgId);
            List<UpmsUserOrganization> userOrganizations = upmsUserOrganizationService.selectByExample(upmsUserOrganizationExample);

            List<UpmsUser> upmsUsers = Collections.emptyList();
            if(CollectionUtils.isNotEmpty(userOrganizations)) {
                upmsUsers = upmsUserService.getUserListByUserIds(userOrganizations.stream().map(UpmsUserOrganization::getUserId).map(String::valueOf).collect(Collectors.joining(",")));
            }

            String result = ItvJsonUtil.listToJson(upmsUsers);

            RedisUtil.set(UpmsConstant.ORG_USER_LIST_REDIS_PREFIX + orgId, result, UpmsConstant.ORG_USER_LIST_REDIS_EXPIRE * 60);
            return upmsUsers;
        } else {
            return ItvJsonUtil.jsonToList(operableListRedis, UpmsUser.class);
        }
    }

    /**
     * 获取角色可管理的员工列表（包含角色信息）
     *
     * @param userName
     * @param orgId
     * @return
     */
    @Override
    public JSONObject getOperableUserListWithRole(String userName, Integer orgId, Integer offset, Integer limit) {
        UpmsUser upmsUser = selectUpmsUserByUsername(userName);
        JSONObject resultData = new JSONObject();
        List<UpmsUserResultDTO> resultList = new ArrayList<>();
        List<UpmsRole> upmsRoles = getOperableRoles(upmsUser.getUserId());
        if (CollectionUtils.isEmpty(upmsRoles)) {
            resultData.put("list", resultList);
            resultData.put("total", 0);
            return resultData;
        }

        Integer total = 0;

        List<Integer> operableRoleIds = upmsRoles.stream().map(UpmsRole::getRoleId).collect(Collectors.toList());

        List<Integer> userIds = upmsUserOrganizationService.getUserIdsByOrgId(orgId);

        if (CollectionUtils.isNotEmpty(userIds) && CollectionUtils.isNotEmpty(operableRoleIds)) {


            UpmsUserRoleExample userRoleExample = new UpmsUserRoleExample();
            userRoleExample.createCriteria().andRoleIdIn(operableRoleIds).andUserIdIn(userIds);
            userRoleExample.setOrderByClause("role_id ASC");
            List<UpmsUserRole> userRoles = upmsUserRoleService.selectByExample(userRoleExample);

            List<Integer> operableUserIds = userRoles.stream().map(UpmsUserRole::getUserId).distinct().filter(p -> !p.equals(upmsUser.getUserId())).collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(operableUserIds)) {
                UpmsUserExample upmsUserExample = new UpmsUserExample();
                upmsUserExample.createCriteria().andUserIdIn(operableUserIds);
                upmsUserExample.setOrderByClause("locked ASC, ctime ASC");

                List<UpmsUser> operableList = upmsUserService.selectByExample(upmsUserExample);
                total = upmsUserRoleService.countByExample(userRoleExample);

                Map<Integer, UpmsRole> upmsRoleMap = upmsRoles.stream().collect(Collectors.toMap(UpmsRole::getRoleId, Function.identity()));
                Map<Integer, Integer> upmsUserRoleMap = userRoles.stream().collect(Collectors.toMap(UpmsUserRole::getUserId, UpmsUserRole::getRoleId));
                operableList.forEach(p -> {
                    UpmsUserResultDTO result = new UpmsUserResultDTO();
                    BeanUtils.copyProperties(p, result, UpmsUserResultDTO.class);
                    Integer roleId = upmsUserRoleMap.get(p.getUserId());
                    if (roleId != null) {
                        result.setRoleId(roleId);
                        result.setRoleName(upmsRoleMap.get(roleId).getName());
                        result.setRoleTitle(upmsRoleMap.get(roleId).getTitle());

                        resultList.add(result);
                    }
                });
            }
        }

        List<UpmsUserResultDTO> sortedList = resultList.stream().sorted(Comparator.comparing(UpmsUserResultDTO::getLocked)
                .thenComparing(Comparator.comparing(UpmsUserResultDTO::getRoleId)
                        .thenComparing(Comparator.comparing(UpmsUserResultDTO::getCtime)))).skip(offset).limit(limit).collect(Collectors.toList());

        resultData.put("list", sortedList);
        resultData.put("total", total);
        return resultData;
    }

    /**
     * 获取角色可管理的员工列表（userId, username）
     *
     * @param userName
     * @param orgId
     * @return
     */
    @Override
    public JSONArray getOperableListJson(String userName, Integer orgId) {

        // 缓存中获取
        String operableListRedis = RedisUtil.get(UpmsConstant.USER_OPERABLE_LIST_JSON_REDIS_PREFIX + userName);

        if (StringUtils.isEmpty(operableListRedis)) {

            JSONArray result = new JSONArray();

            List<UpmsUser> upmsUsers = getOperableUserList(userName, orgId);
            if (CollectionUtils.isNotEmpty(upmsUsers)) {
                upmsUsers.forEach(p -> {
                    JSONObject item = new JSONObject();
                    item.put("userId", p.getUserId());
                    item.put("username", p.getUsername());
                    result.add(item);
                });
            }

            RedisUtil.set(UpmsConstant.USER_OPERABLE_LIST_JSON_REDIS_PREFIX + userName, result.toString(), UpmsConstant.USER_OPERABLE_LIST_JSON_REDIS_EXPIRE * 60);
            return result;
        } else {
            return JSONArray.fromObject(operableListRedis);
        }
    }

    @Override
    @Transactional
    public Integer createEmployee(UpmsUser upmsUser, Integer orgId, Integer roleId) {
        DynamicDataSource.setDataSource(DataSourceEnum.MASTER.getName());

        long time = System.currentTimeMillis();
        String salt = UUID.randomUUID().toString().replaceAll("-", "");
        upmsUser.setSalt(salt);
        upmsUser.setPassword(MD5Util.md5(upmsUser.getPassword() + upmsUser.getSalt()));
        upmsUser.setCtime(time);
        upmsUser = upmsUserService.createUser(upmsUser);
        if (null == upmsUser) {
            return null;
        }

        UpmsUserOrganization upmsUserOrganization = new UpmsUserOrganization();
        upmsUserOrganization.setOrganizationId(orgId);
        upmsUserOrganization.setUserId(upmsUser.getUserId());
        upmsUserOrganizationService.insertSelective(upmsUserOrganization);

        UpmsUserRole upmsUserRole = new UpmsUserRole();
        upmsUserRole.setRoleId(roleId);
        upmsUserRole.setUserId(upmsUser.getUserId());
        upmsUserRoleService.insertSelective(upmsUserRole);

        LOGGER.info("新增用户，主键：userId={}", upmsUser.getUserId());
        return upmsUser.getUserId();
    }

    /**
     * 写入操作日志
     *
     * @param record
     * @return
     */
    @Override
    public int insertUpmsLogSelective(UpmsLog record) {
        return upmsLogMapper.insertSelective(record);
    }

    /**
     * 发送验证码
     *
     * @param mobile
     * @return
     */
    @Override
    public boolean sendSmsCode(String mobile) {
        String smsCode = Tools.getOneNineRand(6);
        return sendSmsLsm(mobile, smsCode);
    }

    /**
     * 校验验证码
     *
     * @param mobile
     * @param smsCode
     * @return
     */
    @Override
    public boolean verifySmsCode(String mobile, String smsCode) {
        String code = RedisUtil.get(UpmsConstant.SMS_REDIS_PREFIX + mobile);
        if (StringUtils.isEmpty(code)) {
            return false;
        } else if (code.equals(smsCode)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean sendSmsLsm(String mobilePhone, String smsCode) {
        HttpClient httpclient = HttpClients.createDefault();

        try {
            HttpPost httppost = new HttpPost(UpmsConstant.SMS_APP_LSM_URL);
            httppost.setHeader("Content-Type", "application/x-www-form-urlencoded");

            String userPassword = UpmsConstant.SMS_APP_LSM_ACCOUNT + ":key-" + UpmsConstant.SMS_APP_LSM_PASSWORD;
            httppost.setHeader("Authorization", "Basic " + Base64.encodeBase64String(userPassword.getBytes()));

            String message = MessageFormat.format(UpmsConstant.SMS_APP_LSM_CONTENT,
                    smsCode, UpmsConstant.SMS_VERIFY_CODE_EXPIRE);

            List<NameValuePair> nvps = new ArrayList<>();
            nvps.add(new BasicNameValuePair("mobile", mobilePhone));
            nvps.add(new BasicNameValuePair("message", message));
            httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

            HttpResponse res = httpclient.execute(httppost);
            if (res.getStatusLine().getStatusCode() != 200) {
                return false;
            }

            String resJson = EntityUtils.toString(res.getEntity());
            LOGGER.info("sendSmsLsm mobilePhone:" + mobilePhone + " smsCode:" + smsCode + " resJson:" + resJson);

            // close connection
            EntityUtils.consume(res.getEntity());

            JSONObject jsonResult = JSONObject.fromObject(resJson);
            int error = jsonResult.getInt("error");
            // sms send success, save sms code to redis
            if (error == 0) {
                RedisUtil.set(UpmsConstant.SMS_REDIS_PREFIX + mobilePhone, smsCode, UpmsConstant.SMS_VERIFY_CODE_EXPIRE * 60);
                return true;
            }
        } catch (Exception e) {
            LOGGER.error("sendSmsLsm exception", e);
        } finally {
            httpclient.getConnectionManager().shutdown();
        }

        return false;
    }

    /**
     * 获取七牛云上传token
     *
     * @param userId
     * @param bucket
     * @param module
     * @return
     */
    @Override
    public Map<String, Object> getQiniuToken(String userId, String bucket, String module) {
        return qiniuUploadServiceImpl.getQiniuToken(userId, bucket, module);
    }

    /**
     * 直接上传网络图片
     *
     * @param url
     * @param bucket
     * @return
     */
    @Override
    public Map<String, Object> uploadUrlFile(String url, String bucket) {
        return qiniuUploadServiceImpl.uploadUrlFile(url, bucket);
    }



    @Override
    public List<UpmsUser> getUserListByUserIds(String userIds) {
        return upmsUserService.getUserListByUserIds(userIds);
    }

    public static void main(String[] args) {
        System.out.println(StringEscapeUtils.unescapeHtml("des fibres de polyethylene de haute résistance, à haute résistance, de poids léger, facile à transporter et à utiliser. Lorsque \n" +
                " corde grain emballés de façon serrée est clair, résistant à l&#39;usure, une faible absorption d&#39;eau, une faible ductilité et résistance à la corrosion. \n" +
                " Notez que l&#39;utilisation, comme matériau de friction, laissant une longueur suffisante pour empêcher un mauvais alignement ou une note nouée \n" +
                " Portée: escalade en plein air, les grottes d&#39;ancrage de la protection, l&#39;aide d&#39;escalade et noueux."));
//        System.out.println(ss);
    }
}